//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include <algorithm>
#ifdef LINUX
#include <wx/imaglist.h>
#endif

#include "guilogic.h"
#include "syslogtab.h"
#include "syslogsrv.h"
#include "rtevents.h"
#include "searchpanel.h"
#include "rcmdapp.h"

enum cListColumns  { cListColumnPrio=0, cListColumnRecTime, cListColumnEvtTime, cListColumnFrom, cListColumnTag, cListColumnFacil, cListColumnEvt};

enum cIDS
{
	ID_LIST=8001
};

static SEARCHFIELD sf[]=
{
	{ cSearchTypeNumeric,	cListColumnPrio,	_("Priority")		},
	{ cSearchTypeString,	cListColumnRecTime,	_("Receive Time")	},
	{ cSearchTypeString,	cListColumnEvtTime,	_("Event Time")		},
	{ cSearchTypeString,	cListColumnFrom,	_("From")			},
	{ cSearchTypeString,	cListColumnTag,		_("Tag")			},
	{ cSearchTypeNumeric,	cListColumnFacil,	_("Facility")		},
	{ cSearchTypeString,	cListColumnEvt,		_("Event")			},
};

BEGIN_EVENT_TABLE(CSysLogTab, wxPanel)
	EVT_LIST_COL_CLICK(ID_LIST, CSysLogTab::OnColumn)
	EVT_ON_SYSLOGMSG(wxID_ANY, CSysLogTab::OnNewLogMsg)
END_EVENT_TABLE()

CSysLogTab::CSysLogTab(class wxWindow* pParent):wxPanel(pParent),
	m_bSortAsc(true), CSearchImpl<CSysLogTab>(*this)
{
	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);

	m_pSearch = new CSearchPanel(this, this);
	sizer->Add(m_pSearch, 0, wxALL|wxEXPAND);

	m_nSortCol= cListColumnRecTime;

	m_pList = new CLogCtrl<CSysLogTab>(this, ID_LIST);
	SetSearchListPtr(m_pList);

	sizer->Add(m_pList, 1, wxALL|wxEXPAND);

	for (size_t i=0;i<sizeof(sf)/sizeof(sf[0]);i++)
	{
		m_pList->InsertColumn( sf[i].iFieldID, sf[i].strLabel );
	}

	m_pList->SetColumnWidth( cListColumnPrio,	110	);
	m_pList->SetColumnWidth( cListColumnRecTime,110 );
	m_pList->SetColumnWidth( cListColumnEvtTime,110	);
	m_pList->SetColumnWidth( cListColumnFrom,	100	);
	m_pList->SetColumnWidth( cListColumnTag,	80	);
	m_pList->SetColumnWidth( cListColumnFacil,	60	);
	m_pList->SetColumnWidth( cListColumnEvt,	220 );

	SetSizer(sizer);

	m_pImageList = new wxImageList(16, 16);

	wxImage icnTmp;
	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("emergency.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("alert.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}
	
	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("critical.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("error.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("warning.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("notice.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("informational.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("debug.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	m_pList->SetImageList(m_pImageList, wxIMAGE_LIST_SMALL);
	m_pList->SetImageList(m_pImageList, wxIMAGE_LIST_STATE);
	
}

CSysLogTab::~CSysLogTab(void)
{
}

wxString CSysLogTab::FieldToString( int column, const SYSLOGMSG& entry)
{
	wxString str;

	try
	{
		switch(column)
		{
			case cListColumnRecTime:
			{
				wxDateTime dt(entry.tm);
				str = dt.Format(_("%c"));
			}
			break;

			case cListColumnEvtTime:
				str = entry.strTime;
			break;

			case cListColumnFrom:
				str = entry.strHost;
			break;

			case cListColumnFacil:
				str = GenerateFacilName(entry.iFacil);
			break;

			case cListColumnPrio:
				str = GeneratePrioName(entry.iPrio);
			break;

			case cListColumnEvt:
				str = entry.strMsg;
			break;

			case cListColumnTag:
				str = entry.strTag;
			break;
		}
	}
	catch (...)
	{

	}

	return str;
}

wxString CSysLogTab::GetItemText(long item, long column, wxWindowID id)
{
	SYSLOGMSG& entry = m_storage[item];
	return FieldToString(column, entry);
}

wxString CSysLogTab::GenerateFacilName(int iFac)
{
	wxString str;

	switch(iFac)
	{
		case 0: return _("kernel");
		case 1: return _("user-level");
		case 2: return _("mail system");
		case 3: return _("system daemons");
		case 4: return _("security/authorization");
		case 5: return _("syslogd");
		case 6: return _("printer");
		case 7: return _("network news");
		case 8: return _("UUCP subsystem");
		case 9: return _("clock daemon");
		case 10: return _("security/authorization");
		case 11: return _("FTP daemon");
		case 12: return _("NTP subsystem");
		case 13: return _("log audit");
		case 14: return _("log alert");
		case 15: return _("clock daemon");
		case 16: return _("local0");
		case 17: return _("llocal1");
		case 18: return _("local2");
		case 19: return _("local3");
		case 20: return _("local4");
		case 21: return _("local5");
		case 22: return _("local6");
		case 23: return _("local7");

		default:
		{
			str = wxString::Format(wxT("%d"), iFac);
		}
		break;
	}

	return str;
}

wxString CSysLogTab::GeneratePrioName(int iPrio)
{
	wxString str;

	switch(iPrio)
	{
		case 0:  return _("Emergency");
		case 1:  return _("Alert");
		case 2:  return _("Critical");
		case 3:  return _("Error");
		case 4:  return _("Warning");
		case 5:  return _("Notice");
		case 6:  return _("Informational");
		case 7:  return _("Debug");

		default:
		{
			str = wxString::Format(wxT("%d"), iPrio);
		}
		break;
	}

	return str;
}


//////////////////////////////////////////////////////////////////////////
//Sort
struct CSortHelper
{
	CSortHelper(int col, bool bSortAsc):m_col(col), m_bSortAsc(bSortAsc) {}

	bool operator() (const SYSLOGMSG& a, const SYSLOGMSG& b)
	{
		bool bRes(false);

		switch (m_col)
		{
			case cListColumnRecTime:
				bRes = a.tm<b.tm;
			break;

			case cListColumnEvtTime:
				bRes = a.strTime<b.strTime;
			break;

			case cListColumnFrom:
				bRes = a.strHost<b.strHost;
			break;

			case cListColumnTag:
				bRes = a.strTag<b.strTag;
			break;

			case cListColumnFacil:
				bRes = a.iFacil<b.iFacil;
			break;

			case cListColumnPrio:
				bRes = a.iPrio<b.iPrio;
			break;

			case cListColumnEvt:
				bRes = a.strMsg<b.strMsg;
			break;
		}
	
		return m_bSortAsc?bRes:!bRes;
	}

private:
	int			 m_col;
	bool		 m_bSortAsc;
};

void CSysLogTab::DoSort()
{
	CSortHelper sorter(m_nSortCol, m_bSortAsc);
	std::sort( m_storage.begin(), m_storage.end(), sorter);
}

//////////////////////////////////////////////////////////////////////////
void CSysLogTab::DoFilter()
{
	wxString strVal;

	for(size_t i=0;i<m_newEntries.size();i++)
	{
		bool bValid(true);

		for (size_t j=0;j<sizeof(sf)/sizeof(sf[0]);j++)
		{
			strVal = FieldToString( sf[j].iFieldID, m_newEntries[i] );

			if ( !m_pSearch->Match(sf[j], strVal) )
			{
				bValid = false;
				break;
			}
		}

		if ( bValid )
		{
			m_storage.push_back( m_newEntries[i] );
		}
	}

	m_newEntries.erase( m_newEntries.begin(), m_newEntries.end() );
}

void CSysLogTab::ProcessData()
{
	try
	{
		ResetSearchPos();

		DoFilter();

		DoSort();

		((CLogCtrl<CSysLogTab>*)m_pList)->DataChanged();
	}
	catch(...)
	{
	}
}



void CSysLogTab::OnNewLogMsg(wxCommandEvent& event)
{
	ProcessData();	
}

void CSysLogTab::OnColumn(wxListEvent& ev)
{
	m_bSortAsc=!m_bSortAsc;

	m_nSortCol = ev.m_col;

	DoSort();
	
	((CLogCtrl<CSysLogTab>*)m_pList)->DataChanged();
}

////////////////////////////////////////////////////////////////////////////
//ISearchProvider
void CSysLogTab::GetSearchFields(SEARCHFIELD*& sfRes, size_t& sz)
{
	sfRes = sf;
	sz = sizeof(sf)/sizeof(sf[0]);
}

void CSysLogTab::OnFilterChanged()
{
	m_storage.erase( m_storage.begin(), m_storage.end() );
	m_newEntries = m_beforeFilter;

	ProcessData();
}

////////////////////////////////////////////////////////////////////////////
//IGUIBind
void CSysLogTab::OnResponse(const COMMANDRESPONSE& resp)
{
	m_newEntries.push_back( resp.msg );
	m_beforeFilter.push_back( resp.msg );

	ProcessData();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//IGUISupport
wxString CSysLogTab::Save()
{
	wxString str;

	for( size_t i=0;i<m_storage.size();i++ )
	{
		const SYSLOGMSG& entry = m_storage[i];
		str+=entry.strRaw;
		str+=wxT("\n");
	}

	return str;
}

void CSysLogTab::Clear()
{
	m_beforeFilter.erase(m_beforeFilter.begin(), m_beforeFilter.end());
	m_storage.erase(m_storage.begin(), m_storage.end());
	

	ProcessData();
	Refresh();
}

int CSysLogTab::GetItemImage(long item, wxWindowID id/*=wxID_ANY*/)
{
	return -1;
}

int	CSysLogTab::GetItemColumnImage(long item, long column, wxWindowID id/*=wxID_ANY*/)
{
	if ( 0==column )
	{
		SYSLOGMSG& entry = m_storage[item];
		return entry.iPrio;
	}
	else
	{
		return -1;
	}
}
